/**
 * 
 */
package org.ztest.concurrent;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;

/**
 * 单线程执行器
 * @author lhl
 *
 * 2015年10月27日 下午2:23:46
 */
public class SingleThreadExecutor implements Executor
{
	private volatile boolean isRunning;
	
	private Thread thread;
	private BlockingQueue<Runnable> taskQueue;
	
	public SingleThreadExecutor(ThreadFactory threadFactory)
	{
		taskQueue = new LinkedBlockingQueue<>();
		
		thread = threadFactory.newThread(new Runnable ()
		{
			@Override
			public void run()
			{
				try
				{
					SingleThreadExecutor.this.run();
				}
				catch (Throwable t)
				{
					t.printStackTrace();
				}
			}
		});
	}

	/**
	 * 运行主逻辑
	 */
	public void run()
	{
		for (;;)
		{
			try
			{
				runAllTasks();
			}
			catch (Throwable t)
			{
				t.printStackTrace();
			}
		}
	}
	
	public void runAllTasks() throws InterruptedException
	{
		Runnable task = takeTask();
		if (task == null)
		{
			return;
		}
		
		for (;;)
		{
			try
			{
				task.run();
			}
			catch (Throwable t)
			{
				t.printStackTrace();
			}
			
			task = takeTask();
			if (task == null)
			{
				return;
			}
		}
	}
	
	private Runnable takeTask() throws InterruptedException
	{
		return taskQueue.take();
	}
	
	public boolean inEventLoop()
	{
		return inEventLoop(Thread.currentThread());
	}
	
	public boolean inEventLoop(Thread thread)
	{
		return thread == this.thread;
	}
	
	public void addTask(Runnable task) throws NullPointerException
	{
		if (task == null)
		{
			throw new NullPointerException("task");
		}
		if (isShutdown())
		{
			return;
		}
		
		taskQueue.add(task);
	}
	
	public boolean removeTask(Runnable task)
	{
		return taskQueue.remove(task);
	}
	
	public void startThread()
	{
		if (!isRunning)
		{
			thread.start();
			isRunning = true;
		}
	}
	
	public void stopThread()
	{
		if (isRunning)
		{
			isRunning = false;
		}
	}
	
	public boolean isShutdown()
	{
		return !isRunning;
	}

	@Override
	public void execute(Runnable command) throws NullPointerException
	{
		if (command == null)
		{
			throw new NullPointerException("command");
		}
		
		boolean inEventLoop = inEventLoop();
		if (inEventLoop)
		{
			addTask(command);
		}
		else
		{
			startThread();
			addTask(command);
			if (isShutdown())
			{
				removeTask(command);
			}
		}
	}
}
